package com.carlinfo.common.dao.impl;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.carlinfo.common.dao.IBaseDao;
import com.carlinfo.common.util.DbUtil;
import com.carlinfo.common.util.PageInfoUtil;

/**
 *  所有公共Dao实现类的代码
 * 一旦实现了一个接口,要把接口中所有的方法都实现一遍;
 * 如果不想实现,那就是不孝子,变成抽象类
 * 
 * @author WangshMac
 * @param <T>
 */
public abstract class BaseDaoImpl<T> implements IBaseDao<T>
{
	protected Logger logger = LogManager.getLogger() ;
	/* pojo的指定类型 */
	protected Object pojoObj;
	
	/**
	 * 查询一条记录
	 * @param sql 有占位符
	 * @param paramsList 占位符对应的值
	 * @return
	 */
	protected T findOneJDBC(String sql , List<Object> paramsList)
	{
		/* 加载驱动+获取链接 */
		DbUtil dbUtil = DbUtil.getInstance(); 
		Connection conn = dbUtil.getConn();
		PreparedStatement pst = null ; 
		ResultSet rs = null ; 
		try
		{
			this.logger.info("sql:{},paramList:{}",sql,paramsList);
			/* sql语句对象 */
			pst = conn.prepareStatement(sql);
			/* 设置占位符,直接循环paramList
			 * paramList的下标值(从0开始)就是问号的索引值(从1开始)
			 *  */
			int count = 1;
			for (Iterator iterator = paramsList.iterator(); iterator.hasNext();)
			{
				Object val = (Object) iterator.next();
				/* 参数1:是第几个?
				 * 参数2:是值 */
				pst.setObject(count, val);
				count ++ ; 
			}
			/* 查询 */
			rs = pst.executeQuery();
			/* 查询单条,还是多条 */
			if(rs.next())
			{
				/**
				 * 此代码可是在父类,
				 * converFromDb:在子类 
				 * 如果调用此方法呢?
				 */
				T t = converFromDb(rs);
				return t ; 
			}
		} catch (SQLException e)
		{
			e.printStackTrace();
		} finally
		{
			dbUtil.closeAll(conn, pst, rs);
		}
		return null ; 
	}
	
	/**
	 * @param pageInfoUtil 分页对象
	 * @param sql sql语句:只有sql语句和占位符
	 * @param paramsList 占位符对应的值
	 * @return	查询的记录
	 */
	protected List<T> findListByPage(PageInfoUtil pageInfoUtil,
			String sql,List<Object> paramsList)
	{
		/* 存储的是结果 */
		List<T> resultList = new ArrayList<T>();
		/* JDBC查询多条皇上 */
		DbUtil dbUtil = DbUtil.getInstance();
		/* 加载驱动+获取链接 */
		Connection conn = dbUtil.getConn() ; 
		PreparedStatement pst = null; 
		ResultSet rs = null ; 
		try
		{
			/*
			 * 分页:
			 * 	查询总记录数
			 * 	查询分页
			 * 不分页:
			 * 	查询记录
			 * */
			int count = 1 ; 
			
			if(pageInfoUtil != null)
			{
				/*----------计算总记录数--------*/
				/* 原来的SQL语句是:select * from a_king limit ? , ?
				 * 总条数:                           select count(*) from a_king;
				 *   */
				String fromIndex = "from" ;
				String countSQL = "select count(*) " + 
						sql.substring(sql.indexOf(fromIndex), sql.length());
				this.logger.info("countSQL:{},参数:{}",countSQL,paramsList);
				/* 又要执行SQL语句 */
				pst = conn.prepareStatement(countSQL);
				/* 为占位符赋值 */
				count = 1 ; 
				for (Iterator iterator = paramsList.iterator(); iterator.hasNext();)
				{
					Object val = (Object) iterator.next();
					pst.setObject(count, val);
					count ++ ; 
				}
				/* 执行sql语句 */
				rs = pst.executeQuery();
				if(rs.next())
				{
					int totalRecord = rs.getInt(1);
					/* 赶紧把查询出的总条数赋值给pageInfoUtil */
					pageInfoUtil.setTotalRecord(totalRecord);
				}
				/* 关闭 */
				rs.close();
				pst.close();
				
				/*----------以下是正常测试的分页--------*/
				/* 写?的时候想想如何赋值
				 * 从PageInfo中获取---(传参)
				 *  */
				sql = sql + " limit ? , ? ";
				paramsList.add(pageInfoUtil.getCurrentRecord());
				paramsList.add(pageInfoUtil.getPageSize());
			}
			
			return this.findList(sql, paramsList);
		} catch (SQLException e)
		{
			logger.error("皇上操作:查询多条记录失败",e);
		} finally
		{
			dbUtil.closeAll(conn, pst, rs);
		}
		/* 比返回Null强,不会跑空指针 */
		return Collections.emptyList();
	}
	
	/**
	 * 查询多条记录,不分页
	 * @param sql
	 * @param paramsList
	 * @return
	 */
	protected List<T> findList(String sql, List<Object> paramsList)
	{
		/* 存储的是结果 */
		List<T> resultList = new ArrayList<T>();
		/* JDBC查询多条皇上 */
		DbUtil dbUtil = DbUtil.getInstance();
		/* 加载驱动+获取链接 */
		Connection conn = dbUtil.getConn() ; 
		PreparedStatement pst = null; 
		ResultSet rs = null ; 
		try
		{
			/*
			 * 分页:
			 * 	查询总记录数
			 * 	查询分页
			 * 不分页:
			 * 	查询记录
			 * */
			int count = 1 ; 
			
			this.logger.info("sql:{},参数:{}",sql,paramsList);
			/* 获取SQL语句对象 */
			pst = conn.prepareStatement(sql);
			/* 为占位符赋值 */
			count = 1 ; 
			for (Iterator iterator = paramsList.iterator(); iterator.hasNext();)
			{
				Object val = (Object) iterator.next();
				pst.setObject(count, val);
				count ++ ; 
			}
			/* 执行SQL语句 */
			rs = pst.executeQuery();
			/* 如果是查询一条,用if
			 * 如果是查询多条,用while */
			while(rs.next())
			{
				T t = this.converFromDb(rs);
				/* 放到集合中 */
				resultList.add(t);
			}
			/* 将集合返回 */
			return resultList ; 
		} catch (SQLException e)
		{
			logger.error("皇上操作:查询多条记录失败",e);
		} finally
		{
			dbUtil.closeAll(conn, pst, rs);
		}
		/* 比返回Null强,不会跑空指针 */
		return Collections.emptyList();
	}
	
	/**
	 * CUD
	 * @return 此条sql语句影响的条数
	 */
	protected int executeJDBC(String sql,List<Object> paramsList)
	{
		/* JDBC的更新
		 * 先写JDBC的架构 */
		/* 加载驱动+获取链接 */
		DbUtil dbUtil = DbUtil.getInstance() ;
		Connection conn = dbUtil.getConn();
		PreparedStatement pst = null ; 
		try
		{
			this.logger.info("sql:{},paramList:{}",sql,paramsList);
			/* 准备SQL语句对象 */
			pst = conn.prepareStatement(sql);
			int count = 1; 
			/* 为占位符赋值 */
			for (Iterator iterator = paramsList.iterator(); iterator.hasNext();)
			{
				Object val = (Object) iterator.next();
				/* 问号的个数: */
				pst.setObject(count, val);
				count ++ ; 
			}
			/* 执行SQL语句 */
			int res = pst.executeUpdate();
			/* 一定要返回 */
			return res ; 
		} catch (SQLException e)
		{
			this.logger.error("朝代删除操作失败了;",e);
		} finally
		{
			dbUtil.closeAll(conn, pst, null);
		}
		return 0 ; 
	}
	
	/**
	 * CUD
	 * @return 此条sql语句影响的条数
	 */
	protected int executeJDBC(String sql,List<Object> paramsList,
			Map<String, Object> resultMap)
	{
		/* JDBC的更新
		 * 先写JDBC的架构 */
		/* 加载驱动+获取链接 */
		DbUtil dbUtil = DbUtil.getInstance() ;
		Connection conn = dbUtil.getConn();
		PreparedStatement pst = null ;
		ResultSet rs = null; 
		try
		{
			this.logger.info("sql:{},paramList:{}",sql,paramsList);
			/* 准备SQL语句对象 */
			pst = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
			int count = 1; 
			/* 为占位符赋值 */
			for (Iterator iterator = paramsList.iterator(); iterator.hasNext();)
			{
				Object val = (Object) iterator.next();
				/* 问号的个数: */
				pst.setObject(count, val);
				count ++ ; 
			}
			/* 执行SQL语句 */
			int res = pst.executeUpdate();
			/*
			 * 将res(此sql语句影响的条数,做为方法的返回值)
			 * id:设置到参数中的id(因为保存不需要设置主键)
			 * */
			rs = pst.getGeneratedKeys();
			if(rs.next())
			{
				/* 把主键放到了key中 */
				int id = rs.getInt(1);
				resultMap.put("id", id);
			}
			/* 一定要返回 */
			return res ; 
		} catch (SQLException e)
		{
			this.logger.error("朝代删除操作失败了;",e);
		} finally
		{
			dbUtil.closeAll(conn, pst, null);
		}
		return 0 ; 
	}

	/**
	 * 将数据库里面的记录变成POJO;
	 * @param rs
	 * @return
	 */
	protected T converFromDb(ResultSet rs) 
			throws SQLException
	{
		try
		{
			/*----列出pojo中所有的属性----*/
			/* 获取Class对象
			 * 改包名咋办
			 *  */
			/*Class.forName("com.carlinfo.demo.pojo.ADynasty")*/
			Class cla = pojoObj.getClass();
			/* new对象 */
			T valObj = (T) cla.newInstance() ; 
			/* 列出所有的属性 */
			Field[] allFields = cla.getDeclaredFields();
			for (int i = 0; i < allFields.length; i++)
			{
				Field field = allFields[i];
				try
				{
					//this.logger.info("--i:{}--所有的属性:{}--name:{}===>",i,field,field.getName());
					/* 属性的名字,和表的列名一样 */
					String fileName = field.getName() ; 
					
					//this.logger.info("--i:{}--所有的属性:{}--name:{}===>",i,field,fileName);
					if("byte".equalsIgnoreCase(field.getType().getName())
						|| "int".equalsIgnoreCase(field.getType().getName())
						|| "double".equalsIgnoreCase(field.getType().getName())
						|| String.class.getName().equalsIgnoreCase(field.getType().getName())
						|| Date.class.getName().equalsIgnoreCase(field.getType().getName()))
					{
						/* 如果属性要是以Str结尾,不允许使用反射赋值 */
						if(fileName.toLowerCase().endsWith("str"))
						{
							continue ; 
						}
					}else
					{
						continue ; 
					}
					
					/* 获取数据库的值
					 * 整数:是第几列
					 * 字符串:列的别名
					 *  */
					Object val = rs.getObject(fileName);
					/* 将此值设置到对象中
					 * fileName:属性名
					 * 读写器:写:set属性名(属性名的首字母大写)
					 *  */
					String setName = "set" + fileName.substring(0, 1).toUpperCase() 
							+ fileName.substring(1, fileName.length()); 
					/*
					 * setMethod获取到方法对象
					 * 参数1:方法名
					 * 参数2:形参
					 * 方法定义 
					 * */
					Method setMethod = cla.getMethod(setName, field.getType());
					//this.logger.info("==name:{},val:{}===setName:{};class:{}",
					//		field.getName(),val,setName,field.getType());
					/* 处理各别的类型 */
					if("byte".equalsIgnoreCase(field.getType().getName()))
					{
						/* 说明是Byte类型 */
						setMethod.invoke(valObj, Byte.valueOf(val + ""));
					}else if("double".equalsIgnoreCase(field.getType().getName()))
					{
						/* 说明是Byte类型 */
						setMethod.invoke(valObj, Double.valueOf(val + ""));
					}else
					{
						/* 调用方法
						 * 参数1:对象
						 * 参数2:实参;是从数据库中取出来的
						 * 返回值:方法的返回值;set方法有返回值吗?;
						 *  */
						setMethod.invoke(valObj, val);
					}
				}catch(Exception e)
				{
					this.logger.error("赋值失败;",e);
				}
			}
			
			return valObj; 
		} catch (Exception e)
		{
			this.logger.error("从数据库中赋值给对象失败了;",e);
		}
		return null ; 
	}
}
